package com.cq.digitalemployee.shiro.redis;

import lombok.Getter;
import lombok.Setter;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.IOException;
import java.util.*;

/**
 * shiro-redis缓存操作类
 *
 * @author zzz
 */
public class ShiroRedisCache<K, V> implements Cache<K, V> {

    private RedisTemplate redisTemplate;

    @Value("${shiro-redis.prefix}")
    @Getter
    @Setter
    private String prefix = "digitalemployee:session:";

    public ShiroRedisCache(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public ShiroRedisCache(RedisTemplate redisTemplate, String prefix) {
        this.prefix = prefix;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public V get(K k) throws CacheException {
        if (k == null) {
            return null;
        }
        byte[] keyBytes = getKeyBytes(k);
        byte[] valueBytes = (byte[]) redisTemplate.opsForValue().get(keyBytes);
        V value =getValueByBytes(valueBytes);
        return value;
    }

    @Override
    public V put(K k, V v) throws CacheException {
        if (k == null || v == null) {
            return null;
        }
        byte[] keyBytes = getKeyBytes(k);
        byte[] valueBytes = getValueBytes(v);
        redisTemplate.opsForValue().set(keyBytes, valueBytes);
        return null;
    }

    @Override
    public V remove(K k) throws CacheException {
        byte[] keyBytes = getKeyBytes(k);
        V value = get(k);
        redisTemplate.delete(keyBytes);
        return value;
    }

    @Override
    public void clear() throws CacheException {
        redisTemplate.getConnectionFactory().getConnection().flushDb();
    }

    @Override
    public int size() {
        return redisTemplate.getConnectionFactory().getConnection().dbSize().intValue();
    }

    @Override
    public Set<K> keys() {
        byte[] bytes = (prefix+"*").getBytes();
        Set<byte[]> keys = redisTemplate.keys(bytes);
        Set<K> sets = new HashSet<>();
        for (byte[] key:keys) {
            sets.add((K)key);
        }
        return sets;
    }

    @Override
    public Collection<V> values() {
        Set<K> keySet = keys();
        List<V> valueList = new ArrayList<>();
        for(K key : keySet){
            valueList.add(get(key));
        }
        return valueList;
    }

    private byte[] getKeyBytes(K key) {
        if (key instanceof String) {
            String prefix = this.prefix + key;
            return prefix.getBytes();
        } else {
            return SerializeUtil.serialize(key);
        }
    }

    private K getKeyByBytes(byte[] ketBytes){
        return (K) SerializeUtil.unSerialize(ketBytes);
    }

    private byte[] getValueBytes(V value) {
        if (value instanceof String) {
            return ((String) value).getBytes();
        } else {
            byte[] bytes = SerializeUtil.serialize(value);
            return SerializeUtil.serialize(value);
        }
    }

    private V getValueByBytes(byte[] valueBytes){
        return (V) SerializeUtil.unSerialize(valueBytes);
    }
}
